{T}

NestJS Mock数据配置与接口白名单实战

NestJS Mock数据配置与接口白名单实战

学习目标:掌握 Mock 数据配置方案、mockWhiteList 实现、端口动态配置、部分接口 Mock 部分接口真实请求的实现。


一、Mock 数据配置需求背景

1.1 为什么需要 Mock 数据配置

code
Mock 数据配置需求场景:
│
├── 开发阶段
│   ├── 后端接口未开发完成
│   ├── 前端需要模拟数据进行开发
│   ├── 前后端并行开发
│   └── 提高开发效率
│
├── 接口迁移阶段
│   ├── 部分接口已开发完成
│   ├── 部分接口还在开发中
│   ├── 需要部分 Mock 部分真实
│   └── 渐进式迁移
│
└── 环境切换
    ├── 开发环境使用 Mock
    ├── 测试环境使用真实接口
    └── 生产环境使用真实接口

1.2 Mock 配置的核心问题

code
Mock 配置核心问题:
│
├── 问题一:全量 Mock vs 部分 Mock
│   ├── 全量 Mock:所有接口都使用 Mock 数据
│   ├── 部分 Mock:部分接口 Mock,部分接口真实
│   └── 需求:灵活控制哪些接口走 Mock
│
├── 问题二:环境切换
│   ├── 开发环境:可能需要 Mock
│   ├── 测试环境:不需要 Mock
│   └── 需求:通过环境变量控制
│
└── 问题三:接口白名单
    ├── 哪些接口走 Mock
    ├── 哪些接口走真实后端
    └── 需求:配置化管理

1.3 Mock 配置方案对比

方案优点缺点适用场景
方案一:全局开关实现简单不够灵活,只能全量 Mock 或全量真实接口全部未开发或全部开发完成
方案二:接口白名单灵活可控需要配置白名单部分接口 Mock 部分接口真实
方案三:按环境切换自动化需要多个环境配置文件多环境部署

二、方案一:全局 Mock 开关

2.1 实现思路

code
方案一实现思路:
│
├── 第一步:添加环境变量
│   └── .env.local 中添加 MOCK=true
│
├── 第二步:修改 middleware
│   └── 判断 process.env.MOCK === 'true'
│
├── 第三步:控制 Mock 逻辑
│   ├── true:走 Mock 逻辑
│   └── false:走真实接口
│
└── 第四步:测试验证
    ├── MOCK=true:返回 Mock 数据
    └── MOCK=false:返回真实数据

2.2 完整实现代码

环境变量配置

bash
# .env.local
MOCK=true
BASE_URL=http://localhost:3000
PORT=3010

Middleware 实现

typescript
// middleware.ts
export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    externals: {
      inline: ['protobufjs'],
    },
  },
  hooks: {
    'render:setupMiddleware'(app) {
      app.use(async (req, res, next) => {
        const url = req.url || ''
        
        //  判断是否开启 Mock
        if (process.env.MOCK === 'true') {
          //  所有接口都走 Mock 逻辑
          if (url.startsWith('/api')) {
            // Mock 处理逻辑
            const mockData = getMockData(url)
            res.end(JSON.stringify(mockData))
            return
          }
        }
        
        //  否则走真实接口
        next()
      })
    },
  },
})

2.3 方案一优缺点

code
方案一优缺点分析:
│
├──  优点
│   ├── 实现简单
│   ├── 配置简单
│   └── 全局控制
│
└──  缺点
    ├── 不够灵活
    ├── 只能全量 Mock 或全量真实
    └── 无法部分接口 Mock

三、方案二:接口白名单(推荐)

3.1 实现思路

code
方案二实现思路:
│
├── 第一步:配置白名单
│   └── .env.local 中添加 MOCK_WHITE_LIST
│
├── 第二步:解析白名单
│   └── middleware 中读取并解析为数组
│
├── 第三步:判断接口是否在白名单
│   ├── 在白名单:走真实接口
│   └── 不在白名单:走 Mock 逻辑
│
└── 第四步:测试验证
    ├── 白名单接口:返回真实数据
    └── 非白名单接口:返回 Mock 数据

3.2 完整实现步骤

第一步:配置环境变量

bash
# .env.local
#  Mock 白名单:这些接口不走 Mock,走真实接口
MOCK_WHITE_LIST=/api/courses,/api/users

#  后端接口地址
BASE_URL=http://localhost:3000

#  本地服务端口
PORT=3010

第二步:Middleware 实现白名单逻辑

typescript
// middleware.ts
export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    externals: {
      inline: ['protobufjs'],
    },
  },
  hooks: {
    'render:setupMiddleware'(app) {
      app.use(async (req, res, next) => {
        const url = req.url || ''
        
        //  只处理 API 请求
        if (!url.startsWith('/api')) {
          next()
          return
        }
        
        //  获取白名单
        const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
        
        //  提取 URL 路径(去除 Query 参数)
        const urlPath = url.split('?')[0]
        
        //  判断是否在白名单中
        if (whiteList.indexOf(urlPath) !== -1) {
          //  在白名单中,走真实接口
          next()
        } else {
          //  不在白名单中,走 Mock 逻辑
          const mockData = getMockData(urlPath)
          res.end(JSON.stringify(mockData))
        }
      })
    },
  },
})

第三步:端口动态配置

bash
# .env.local
PORT=3010
BASE_URL=http://localhost:3000
MOCK_WHITE_LIST=/api/courses
typescript
// next.config.ts
import type { Config } from 'next'

const config: Config = {
  devServer: {
    //  动态读取端口,默认 3000
    port: parseInt(process.env.PORT || '3000', 10),
  },
}

export default config

第四步:Mock 数据请求到本地端口

typescript
// middleware.ts(完整版)
import { getMockData } from './mock'

export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    externals: {
      inline: ['protobufjs'],
    },
  },
  hooks: {
    'render:setupMiddleware'(app) {
      app.use(async (req, res, next) => {
        const url = req.url || ''
        
        //  只处理 API 请求
        if (!url.startsWith('/api')) {
          next()
          return
        }
        
        //  获取白名单
        const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
        
        //  提取 URL 路径(去除 Query 参数)
        const urlPath = url.split('?')[0]
        
        //  判断是否在白名单中
        if (whiteList.indexOf(urlPath) !== -1) {
          //  在白名单中,走真实接口
          next()
        } else {
          //  不在白名单中,走 Mock 逻辑
          //  Mock 数据请求到本地端口
          const port = process.env.PORT || '3000'
          const mockUrl = `http://localhost:${port}${url}`
          
          try {
            const response = await fetch(mockUrl)
            const data = await response.json()
            res.end(JSON.stringify(data))
          } catch (error) {
            res.statusCode = 500
            res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
          }
        }
      })
    },
  },
})

3.3 接口白名单工作流程

code
接口白名单工作流程:
│
├── 第一步:前端发起请求
│   └── GET /api/courses
│
├── 第二步:Middleware 拦截
│   └── 提取 URL 路径:/api/courses
│
├── 第三步:读取白名单
│   └── whiteList = ['/api/courses', '/api/users']
│
├── 第四步:判断是否在白名单
│   │
│   ├──  在白名单(/api/courses)
│   │   ├── 走真实接口
│   │   ├── 请求:http://localhost:3000/api/v1/courses
│   │   └── 返回真实数据
│   │
│   └──  不在白名单(/api/home)
│       ├── 走 Mock 逻辑
│       ├── 请求:http://localhost:3010/api/home
│       └── 返回 Mock 数据
│
└── 第五步:响应数据给前端
    └── 前端收到数据并渲染

3.4 接口白名单配置示例

配置说明

bash
# .env.local

#  Mock 白名单配置
# 格式:接口路径,多个用逗号分隔
# 这些接口将走真实后端,不走 Mock

# 示例一:单个接口
MOCK_WHITE_LIST=/api/courses

# 示例二:多个接口
MOCK_WHITE_LIST=/api/courses,/api/users,/api/posts

# 示例三:空字符串(所有接口都走 Mock)
MOCK_WHITE_LIST=

# 示例四:不配置(默认所有接口走 Mock)
# 不添加 MOCK_WHITE_LIST 配置项

配置解析逻辑

typescript
//  正确的解析方式
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')

// 结果:
// MOCK_WHITE_LIST='/api/courses' → ['/api/courses']
// MOCK_WHITE_LIST='/api/courses,/api/users' → ['/api/courses', '/api/users']
// MOCK_WHITE_LIST='' → ['']
// 不配置 → ['']

//  判断逻辑
if (whiteList.indexOf(urlPath) !== -1) {
  // 在白名单中
}

四、端口动态配置详解

4.1 为什么需要动态端口

code
端口动态配置需求:
│
├── 问题:硬编码端口不灵活
│   ├── next.config.ts 中端口写死为 3010
│   ├── 不同开发者可能使用不同端口
│   └── 不同环境可能使用不同端口
│
├── 解决:从环境变量读取
│   ├── .env.local 中配置 PORT
│   ├── next.config.ts 动态读取
│   └── 提供默认值
│
└── 好处:
    ├── 灵活配置
    ├── 不同环境不同端口
    └── 避免端口冲突

4.2 完整实现代码

环境变量配置

bash
# .env.local
#  本地服务端口
PORT=3010

#  后端接口地址
BASE_URL=http://localhost:3000

#  Mock 白名单
MOCK_WHITE_LIST=/api/courses

Next.js 配置

typescript
// next.config.ts
import type { Config } from 'next'

const config: Config = {
  devServer: {
    //  方式一:直接读取,提供默认值
    port: parseInt(process.env.PORT || '3000', 10),
    
    //  方式二:更安全的写法
    // port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
  },
}

export default config

parseInt 详解

typescript
// parseInt(string, radix)
// radix:进制,通常使用 10(十进制)

//  示例
parseInt('3010', 10)  // 3010
parseInt('3010')      // 3010(不推荐,可能产生意外结果)
parseInt('abc', 10)   // NaN

//  安全写法
const port = parseInt(process.env.PORT || '3000', 10)

//  更安全的写法(处理 NaN 情况)
const port = parseInt(process.env.PORT || '3000', 10) || 3000

4.3 端口配置最佳实践

code
端口配置最佳实践:
│
├── 1. 使用环境变量
│   ├── .env.local 配置 PORT
│   ├── next.config.ts 读取 PORT
│   └── 提供默认值
│
├── 2. 端口范围选择
│   ├── 开发环境:3000-9999
│   ├── 避免系统保留端口:0-1023
│   └── 避免常用服务端口:80, 443, 3306, 5432
│
├── 3. 常见端口约定
│   ├── 前端:3000, 3001, 3010, 8080
│   ├── 后端:3000, 4000, 5000, 8000
│   └── 数据库:3306 (MySQL), 5432 (PostgreSQL)
│
└── 4. 端口冲突解决
    ├── 查看端口占用:lsof -i :3000
    ├── 杀死进程:kill -9 <PID>
    └── 更换端口:修改 PORT 环境变量

五、完整实战示例

5.1 项目结构

code
前端项目/
├── middleware.ts              # Middleware 配置
├── next.config.ts            # Next.js 配置(端口配置)
├── .env                      # 默认环境变量
├── .env.local                # 本地环境变量
│   ├── PORT=3010
│   ├── BASE_URL=http://localhost:3000
│   └── MOCK_WHITE_LIST=/api/courses
├── server/
│   └── api/
│       ├── courses/
│       │   └── get.ts        # 课程接口(走真实后端)
│       └── home/
│           └── get.ts        # 首页接口(走 Mock)
└── mock/
    └── index.ts              # Mock 数据

5.2 完整配置清单

1. 环境变量配置

bash
# .env.local
# ========================================
# 本地开发环境配置
# ========================================

#  本地服务端口
PORT=3010

#  后端接口地址(NestJS)
BASE_URL=http://localhost:3000

#  Mock 白名单(这些接口不走 Mock,走真实后端)
# 格式:接口路径,多个用逗号分隔
# 示例:/api/courses,/api/users,/api/posts
MOCK_WHITE_LIST=/api/courses

2. Next.js 配置

typescript
// next.config.ts
import type { Config } from 'next'

const config: Config = {
  //  开发服务器配置
  devServer: {
    // 动态读取端口,默认 3000
    port: parseInt(process.env.PORT || '3000', 10),
  },
  
  // 其他配置...
}

export default config

3. Middleware 完整实现

typescript
// middleware.ts
import { getMockData } from './mock'

export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    externals: {
      inline: ['protobufjs'],
    },
  },
  hooks: {
    'render:setupMiddleware'(app) {
      app.use(async (req, res, next) => {
        const url = req.url || ''
        
        //  只处理 API 请求
        if (!url.startsWith('/api')) {
          next()
          return
        }
        
        //  提取 URL 路径(去除 Query 参数)
        const urlPath = url.split('?')[0]
        
        //  获取白名单
        const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')
        
        //  判断是否在白名单中
        if (whiteList.indexOf(urlPath) !== -1) {
          //  在白名单中,走真实接口
          console.log(`[真实接口] ${urlPath} -> 走 NestJS 后端`)
          next()
        } else {
          //  不在白名单中,走 Mock 逻辑
          console.log(`[Mock 接口] ${urlPath} -> 走 Mock 数据`)
          
          // Mock 数据请求到本地端口
          const port = process.env.PORT || '3000'
          const mockUrl = `http://localhost:${port}${url}`
          
          try {
            const response = await fetch(mockUrl)
            const data = await response.json()
            res.setHeader('Content-Type', 'application/json')
            res.end(JSON.stringify(data))
          } catch (error) {
            console.error('Mock data fetch failed:', error)
            res.statusCode = 500
            res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
          }
        }
      })
    },
  },
})

4. Server API 实现(真实接口)

typescript
// server/api/courses/get.ts
export default defineEventHandler(async (event) => {
  //  获取 Query 参数
  const query = getQuery(event)
  
  //  发起请求到后端(NestJS)
  const data = await $fetch(`${process.env.BASE_URL}/api/v1/courses`, {
    method: 'GET',
    query,
  })
  
  return data
})

5. Mock 数据实现

typescript
// mock/index.ts
export const getMockData = (url: string) => {
  //  根据不同的 URL 返回不同的 Mock 数据
  if (url === '/api/home') {
    return {
      code: 200,
      data: {
        banner: [
          { id: 1, title: 'Banner 1', image: '/banner1.jpg' },
          { id: 2, title: 'Banner 2', image: '/banner2.jpg' },
        ],
        recommend: [
          { id: 1, title: '推荐课程 1' },
          { id: 2, title: '推荐课程 2' },
        ],
      },
    }
  }
  
  // 默认返回空数据
  return {
    code: 404,
    message: 'Mock data not found',
  }
}

5.3 测试验证

测试场景一:白名单接口(走真实后端)

bash
# 请求
GET http://localhost:3010/api/courses

# 日志输出
[真实接口] /api/courses -> 走 NestJS 后端

# 响应数据(来自 NestJS)
{
  "code": 200,
  "data": [
    { "id": 11, "name": "课程 1" },
    { "id": 12, "name": "课程 2" }
  ]
}

测试场景二:非白名单接口(走 Mock)

bash
# 请求
GET http://localhost:3010/api/home

# 日志输出
[Mock 接口] /api/home -> 走 Mock 数据

# 响应数据(来自 Mock)
{
  "code": 200,
  "data": {
    "banner": [...],
    "recommend": [...]
  }
}

测试场景三:修改白名单配置

bash
# 修改 .env.local
MOCK_WHITE_LIST=/api/courses,/api/users

# 重启项目
npm run dev

# 测试 /api/courses
GET http://localhost:3010/api/courses
# 结果:走真实后端

# 测试 /api/users
GET http://localhost:3010/api/users
# 结果:走真实后端

# 测试 /api/home
GET http://localhost:3010/api/home
# 结果:走 Mock

六、常见问题与解决方案

6.1 Mock 白名单不生效问题

问题描述:配置了 MOCK_WHITE_LIST,但白名单不生效,所有接口都走 Mock。

原因分析

typescript
//  错误写法:split 前没有处理空值
const whiteList = process.env.MOCK_WHITE_LIST.split(',')
// 如果 MOCK_WHITE_LIST 未配置,会报错

//  正确写法:提供默认值
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')

解决方案

typescript
//  方案一:提供默认值
const whiteList = (process.env.MOCK_WHITE_LIST || '').split(',')

//  方案二:判断是否存在
const whiteList = process.env.MOCK_WHITE_LIST 
  ? process.env.MOCK_WHITE_LIST.split(',') 
  : []

//  方案三:过滤空字符串
const whiteList = (process.env.MOCK_WHITE_LIST || '')
  .split(',')
  .filter(item => item.trim() !== '')

6.2 端口配置不生效问题

问题描述:修改了 .env.local 中的 PORT,但端口没有变化。

原因分析

code
端口配置不生效原因:
│
├── 原因一:未重启项目
│   └── 环境变量修改后需要重启项目
│
├── 原因二:parseInt 转换错误
│   └── parseInt(process.env.PORT) 可能返回 NaN
│
└── 原因三:环境变量未加载
    └── 未使用 --dotenv 指定环境变量文件

解决方案

bash
#  方案一:重启项目
# 修改 .env.local 后,重启项目
npm run dev

#  方案二:指定环境变量文件
# package.json
{
  "scripts": {
    "dev": "next dev --dotenv .env.local"
  }
}

#  方案三:安全的端口配置
# next.config.ts
const config: Config = {
  devServer: {
    port: parseInt(process.env.PORT || '3000', 10) || 3000,
  },
}

6.3 接口路径匹配问题

问题描述:URL 中包含 Query 参数,导致白名单匹配失败。

原因分析

typescript
//  问题:URL 包含 Query 参数
const url = '/api/courses?page=1&size=10'
const whiteList = ['/api/courses']

whiteList.indexOf(url)  // -1(不匹配)

解决方案

typescript
//  方案一:提取 URL 路径(去除 Query 参数)
const urlPath = url.split('?')[0]
whiteList.indexOf(urlPath)  // 0(匹配)

//  方案二:使用 includes 判断
whiteList.some(path => urlPath.includes(path))

//  方案三:使用正则匹配
const regex = new RegExp(whiteList.join('|'))
regex.test(urlPath)

6.4 Mock 数据 404 问题

问题描述:非白名单接口返回 404 错误。

原因分析

code
Mock 数据 404 原因:
│
├── 原因一:Mock 文件未创建
│   └── 需要在 mock/index.ts 中添加对应的 Mock 数据
│
├── 原因二:Mock 路径配置错误
│   └── mockUrl 配置错误
│
└── 原因三:Mock 服务未启动
    └── Mock 数据服务未正常运行

解决方案

typescript
//  方案一:完善 Mock 数据
// mock/index.ts
export const getMockData = (url: string) => {
  const mockDataMap = {
    '/api/home': {
      code: 200,
      data: { banner: [], recommend: [] }
    },
    '/api/users': {
      code: 200,
      data: []
    },
  }
  
  return mockDataMap[url] || { code: 404, message: 'Mock data not found' }
}

//  方案二:正确配置 Mock URL
const port = process.env.PORT || '3000'
const mockUrl = `http://localhost:${port}${url}`

//  方案三:添加错误处理
try {
  const response = await fetch(mockUrl)
  if (!response.ok) {
    throw new Error('Mock data fetch failed')
  }
  const data = await response.json()
  res.end(JSON.stringify(data))
} catch (error) {
  res.statusCode = 500
  res.end(JSON.stringify({ error: 'Mock data fetch failed' }))
}

七、最佳实践总结

7.1 Mock 配置最佳实践

code
Mock 配置最佳实践:
│
├── 1. 使用接口白名单
│   ├── 配置 MOCK_WHITE_LIST
│   ├── 灵活控制哪些接口走 Mock
│   └── 便于渐进式迁移
│
├── 2. 动态端口配置
│   ├── 使用环境变量 PORT
│   ├── 提供默认值
│   └── 避免端口冲突
│
├── 3. 环境变量管理
│   ├── 使用 .env.local 配置本地环境
│   ├── 使用 .env.production 配置生产环境
│   └── 不要将 .env.local 提交到 Git
│
├── 4. Mock 数据管理
│   ├── 统一放在 mock 目录
│   ├── 根据 URL 返回不同 Mock 数据
│   └── Mock 数据要符合真实接口格式
│
└── 5. 日志调试
    ├── 打印接口类型(Mock 或真实)
    ├── 打印请求 URL
    └── 便于调试和问题定位

7.2 环境变量配置最佳实践

code
环境变量配置最佳实践:
│
├── 1. 文件命名规范
│   ├── .env:默认环境变量(所有环境共享)
│   ├── .env.local:本地环境变量(优先级最高)
│   ├── .env.development:开发环境变量
│   └── .env.production:生产环境变量
│
├── 2. 环境变量命名规范
│   ├── 使用大写字母和下划线
│   ├── 语义化命名
│   └── 添加注释说明
│
├── 3. 环境变量优先级
│   ├── .env.local > .env.[environment] > .env
│   ├── 系统环境变量 > 文件环境变量
│   └── 后加载的会覆盖先加载的
│
└── 4. 安全性
    ├── 不要在 .env 中存储敏感信息
    ├── 使用 .env.example 作为模板
    └── 将 .env.local 添加到 .gitignore

7.3 接口迁移最佳实践

code
接口迁移最佳实践:
│
├── 1. 渐进式迁移
│   ├── 先迁移简单接口
│   ├── 逐步迁移复杂接口
│   └── 最后迁移核心接口
│
├── 2. 白名单管理
│   ├── 迁移完成的接口加入白名单
│   ├── 未迁移的接口保持 Mock
│   └── 定期更新白名单
│
├── 3. 测试验证
│   ├── 每迁移一个接口都要测试
│   ├── 对比 Mock 数据和真实数据
│   └── 确保数据格式一致
│
└── 4. 文档更新
    ├── 记录哪些接口已迁移
    ├── 记录迁移过程中的问题
    └── 更新接口文档

八、命令速查表

8.1 环境变量命令速查

命令说明
npm run dev启动开发服务器(加载 .env.development)
npm run dev:local启动本地测试服务器(加载 .env.local)
npm run build构建生产版本(加载 .env.production)
npm run start运行生产版本

8.2 端口相关命令速查

命令说明
lsof -i :3000查看 3000 端口占用情况
kill -9 <PID>杀死占用端口的进程
netstat -an | grep 3000查看 3000 端口状态
npx kill-port 3000使用工具杀死端口

8.3 环境变量读取速查

方法说明示例
process.env.VAR_NAME读取环境变量process.env.PORT
process.env.VAR_NAME || 'default'提供默认值process.env.PORT || '3000'
parseInt(str, 10)转换为整数parseInt('3010', 10)

九、学习要点总结

9.1 核心知识点

code
NestJS Mock 数据配置核心要点:
│
├── 1. Mock 配置方案 
│   ├── 方案一:全局开关(简单但不灵活)
│   ├── 方案二:接口白名单(灵活可控)
│   └── 选择:推荐使用接口白名单方案
│
├── 2. 接口白名单实现 
│   ├── 配置 MOCK_WHITE_LIST
│   ├── 解析为数组
│   ├── 判断 URL 是否在白名单
│   └── 分别处理 Mock 和真实接口
│
├── 3. 端口动态配置 
│   ├── 使用环境变量 PORT
│   ├── parseInt 转换
│   └── 提供默认值
│
├── 4. 环境变量管理 
│   ├── .env.local 本地配置
│   ├── .env.production 生产配置
│   └── 不同环境不同配置
│
├── 5. Mock 数据管理 
│   ├── 统一放在 mock 目录
│   ├── 根据 URL 返回不同数据
│   └── 格式符合真实接口
│
└── 6. 接口迁移流程 
    ├── 配置白名单
    ├── 实现真实接口
    ├── 测试验证
    └── 更新白名单

9.2 重要程度标注

知识点重要程度说明
接口白名单必须掌握,Mock 配置核心
端口动态配置必须掌握,避免端口冲突
环境变量管理必须掌握,不同环境配置
Middleware 实现必须掌握,请求拦截逻辑
Mock 数据管理重要,Mock 数据组织

9.3 学习路径规划

code
学习路径规划:
│
├── 第一阶段:理解概念(1 天)
│   ├── 理解 Mock 配置的两种方案
│   ├── 理解接口白名单的作用
│   └── 理解端口动态配置的意义
│
├── 第二阶段:实践操作(2-3 天)
│   ├── 配置环境变量
│   ├── 实现 Middleware
│   ├── 测试接口白名单
│   └── 测试 Mock 和真实接口切换
│
└── 第三阶段:深入应用(持续)
    ├── 复杂业务场景
    ├── 性能优化
    └── 团队协作规范

十、完整代码清单

10.1 环境变量配置完整代码

bash
# .env.local
# ========================================
# 本地开发环境配置
# ========================================

#  本地服务端口
PORT=3010

#  后端接口地址(NestJS)
BASE_URL=http://localhost:3000

#  Mock 白名单(这些接口不走 Mock,走真实后端)
# 格式:接口路径,多个用逗号分隔
# 示例:/api/courses,/api/users,/api/posts
MOCK_WHITE_LIST=/api/courses
bash
# .env.production
# ========================================
# 生产环境配置
# ========================================

#  生产环境端口
PORT=3000

#  生产环境后端接口地址
BASE_URL=https://api.production.com

#  生产环境不使用 Mock
MOCK_WHITE_LIST=

10.2 Next.js 配置完整代码

typescript
// next.config.ts
import type { Config } from 'next'

const config: Config = {
  //  开发服务器配置
  devServer: {
    // 动态读取端口,默认 3000
    port: parseInt(process.env.PORT || '3000', 10) || 3000,
  },
  
  // 其他配置...
}

export default config

10.3 Middleware 完整代码

typescript
// middleware.ts
import { getMockData } from './mock'

export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    externals: {
      inline: ['protobufjs'],
    },
  },
  hooks: {
    'render:setupMiddleware'(app) {
      app.use(async (req, res, next) => {
        const url = req.url || ''
        
        //  只处理 API 请求
        if (!url.startsWith('/api')) {
          next()
          return
        }
        
        //  提取 URL 路径(去除 Query 参数)
        const urlPath = url.split('?')[0]
        
        //  获取白名单(过滤空字符串)
        const whiteList = (process.env.MOCK_WHITE_LIST || '')
          .split(',')
          .filter(item => item.trim() !== '')
        
        //  判断是否在白名单中
        if (whiteList.indexOf(urlPath) !== -1) {
          //  在白名单中,走真实接口
          console.log(`[真实接口] ${urlPath} -> 走 NestJS 后端`)
          next()
        } else {
          //  不在白名单中,走 Mock 逻辑
          console.log(`[Mock 接口] ${urlPath} -> 走 Mock 数据`)
          
          // Mock 数据请求到本地端口
          const port = process.env.PORT || '3000'
          const mockUrl = `http://localhost:${port}${url}`
          
          try {
            const response = await fetch(mockUrl)
            if (!response.ok) {
              throw new Error(`HTTP error! status: ${response.status}`)
            }
            const data = await response.json()
            res.setHeader('Content-Type', 'application/json')
            res.end(JSON.stringify(data))
          } catch (error) {
            console.error('Mock data fetch failed:', error)
            res.statusCode = 500
            res.setHeader('Content-Type', 'application/json')
            res.end(JSON.stringify({ 
              code: 500, 
              message: 'Mock data fetch failed',
              error: String(error)
            }))
          }
        }
      })
    },
  },
})

10.4 Mock 数据完整代码

typescript
// mock/index.ts

interface MockData {
  code: number
  message?: string
  data?: any
}

/**
 * 根据 URL 返回 Mock 数据
 * @param url 请求 URL
 * @returns Mock 数据
 */
export const getMockData = (url: string): MockData => {
  //  Mock 数据映射表
  const mockDataMap: Record<string, MockData> = {
    // 首页数据
    '/api/home': {
      code: 200,
      data: {
        banner: [
          { id: 1, title: '精品微课', image: '/banner1.jpg', link: '/courses' },
          { id: 2, title: '学习计划', image: '/banner2.jpg', link: '/plans' },
        ],
        recommend: [
          { id: 1, title: '推荐课程 1', cover: '/course1.jpg', price: 99 },
          { id: 2, title: '推荐课程 2', cover: '/course2.jpg', price: 199 },
        ],
        categories: [
          { id: 11, name: '前端开发', count: 120 },
          { id: 12, name: '后端开发', count: 80 },
        ],
      },
    },
    
    // 用户数据
    '/api/users': {
      code: 200,
      data: [
        { id: 1, username: 'user1', email: 'user1@example.com' },
        { id: 2, username: 'user2', email: 'user2@example.com' },
      ],
    },
    
    // 文章数据
    '/api/posts': {
      code: 200,
      data: [
        { id: 1, title: '文章 1', content: '内容 1', author: '作者 1' },
        { id: 2, title: '文章 2', content: '内容 2', author: '作者 2' },
      ],
    },
  }
  
  //  返回对应的 Mock 数据,如果不存在则返回 404
  return mockDataMap[url] || {
    code: 404,
    message: 'Mock data not found',
    data: null,
  }
}

10.5 Server API 完整代码

typescript
// server/api/courses/get.ts
export default defineEventHandler(async (event) => {
  //  获取 Query 参数
  const query = getQuery(event)
  
  //  发起请求到后端(NestJS)
  const data = await $fetch(`${process.env.BASE_URL}/api/v1/courses`, {
    method: 'GET',
    query,
  })
  
  return data
})
typescript
// server/api/users/get.ts
export default defineEventHandler(async (event) => {
  //  获取 Query 参数
  const query = getQuery(event)
  
  //  发起请求到后端(NestJS)
  const data = await $fetch(`${process.env.BASE_URL}/api/v1/users`, {
    method: 'GET',
    query,
  })
  
  return data
})

十一、扩展阅读

11.1 Mock 数据生成工具

使用 Mock.js 生成 Mock 数据

typescript
// mock/index.ts
import Mock from 'mockjs'

export const getMockData = (url: string) => {
  if (url === '/api/users') {
    //  使用 Mock.js 生成随机数据
    return Mock.mock({
      code: 200,
      'data|10': [{
        'id|+1': 1,
        'username': '@cname',
        'email': '@email',
        'avatar': '@image("100x100")',
        'createTime': '@datetime',
      }]
    })
  }
  
  // 其他 Mock 数据...
}

11.2 自动化 Mock 数据

根据接口文档自动生成 Mock 数据

typescript
// mock/auto.ts
import { OpenAPI } from 'openapi-types'

/**
 * 根据 OpenAPI 文档自动生成 Mock 数据
 */
export const generateMockFromOpenAPI = (doc: OpenAPI.Document) => {
  const mockData = {}
  
  // 遍历所有接口
  for (const [path, methods] of Object.entries(doc.paths)) {
    for (const [method, spec] of Object.entries(methods)) {
      const key = `${method.toUpperCase()} ${path}`
      mockData[key] = generateMockFromSchema(spec.responses['200'])
    }
  }
  
  return mockData
}

11.3 团队协作 Mock 规范

code
团队协作 Mock 规范:
│
├── 1. Mock 数据文件组织
│   ├── mock/
│   │   ├── index.ts        # 入口文件
│   │   ├── home.ts         # 首页相关 Mock
│   │   ├── courses.ts      # 课程相关 Mock
│   │   └── users.ts        # 用户相关 Mock
│   └── 统一管理,便于维护
│
├── 2. Mock 数据命名规范
│   ├── 文件名:小写,用连字符分隔
│   ├── 接口名:RESTful 风格
│   └── 数据字段:驼峰命名
│
├── 3. Mock 数据更新流程
│   ├── 接口文档先行
│   ├── Mock 数据符合接口文档
│   ├── 前端基于 Mock 开发
│   └── 后端接口完成后迁移
│
└── 4. Mock 数据版本控制
    ├── 使用 Git 管理 Mock 数据
    ├── 接口变更时同步更新 Mock
    └── 保留历史版本便于回滚

重要提示:Mock 数据配置是前后端并行开发的重要工具,掌握接口白名单、端口动态配置、环境变量管理,对实际项目开发非常重要!推荐使用接口白名单方案,灵活可控,便于渐进式迁移!

下节预告:Next.js 状态管理进阶,深入学习 Pinia 高级用法、数据持久化、状态同步等高级应用!

笔记已按照您的格式规范整理完成,可直接用于学习复习!